﻿///////////////////////////////////////////////////////
//
// XDesigner.ORM 
//
// autohr : yuans
//
// date: 2011-2-23
//
// email  : yyf9989@hotmail.com
// 
//
///////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections ;
using XDesigner.ORM.DOM;

namespace XDesigner.ORM
{
    public class ORMInstanceBuffer
    {
        public ORMInstanceBuffer()
        {
        }

        private bool _IsGlobal = false;
        /// <summary>
        /// This buffer is global buffer
        /// </summary>
        public bool IsGlobal
        {
            get { return _IsGlobal; }
            set { _IsGlobal = value; }
        }


        private class InstanceBufferItem
        {
            public InstanceBufferItem(Guid guid, object key, object ins)
            {
                this.Guid = guid;
                this.KeyValue = key;
                this.Instance = ins;
            }
            public Guid Guid ;
            public object KeyValue ;
            public object Instance ;
        }

        private Dictionary<object, InstanceBufferItem> _InstanceMap =
            new Dictionary<object, InstanceBufferItem>();

        private Dictionary<Type, Dictionary<string, InstanceBufferItem>> _Buffer
            = new Dictionary<Type, Dictionary<string , InstanceBufferItem>>();

        public object GetInstance(object key, Type instanceType)
        {
            if (key == null || DBNull.Value.Equals(key))
            {
                return null;
            }
            string strKey = Convert.ToString(key);
            lock (this)
            {
                Dictionary<string, InstanceBufferItem> list = GetItems(instanceType);
                if (list != null)
                {
                    if (list.ContainsKey(strKey))
                    {
                        return list[strKey].Instance;
                    }
                }
            }//lock
            return null;
        }

        private Dictionary<string, InstanceBufferItem> GetItems(Type instanceType)
        {
            foreach (Type keyType in _Buffer.Keys)
            {
                if (instanceType.Equals(keyType) 
                    || keyType.IsSubclassOf(instanceType))
                {
                    return _Buffer[keyType];
                }
            }
            return null;
        }

        public void Add( Type instanceType , object keyValue, object instance)
        {
            if ( instance == null || keyValue == null || DBNull.Value.Equals(keyValue))
            {
                return;
            }
            if (instanceType == null)
            {
                return;
            }
            string strKey = Convert.ToString(keyValue);
            lock (this)
            {
                InstanceBufferItem item = null;
                if (_InstanceMap.ContainsKey(instance))
                {
                    item = _InstanceMap[instance];
                    item.KeyValue = keyValue;
                }
                else
                {
                    Dictionary<string, InstanceBufferItem> list = this.GetItems(instanceType);
                    if (list == null)
                    {
                        list = new Dictionary<string, InstanceBufferItem>();
                        _Buffer[instanceType] = list;
                    }
                    InstanceBufferItem newItem = new InstanceBufferItem(
                        Guid.NewGuid(), strKey, instance);
                    list[strKey] = newItem;
                    _InstanceMap[instance] = newItem;
                }
            }
        }

        public void Remove(object instance)
        {
            if (instance == null)
                return;
            if( _InstanceMap.ContainsKey( instance ))
            {
                _InstanceMap.Remove( instance );
                Dictionary<string, InstanceBufferItem> list = this.GetItems(instance.GetType());
                if (list != null)
                {
                    foreach (string key in list.Keys)
                    {
                        InstanceBufferItem item2 = list[key];
                        if (item2.Instance == instance)
                        {
                            list.Remove(key);
                            break;
                        }//if
                    }//foreach
                }
            }//if
        }

        //public void RemoveByKey(object keyValue , Type instanceType )
        //{
        //    if (keyValue == null || DBNull.Value.Equals(keyValue) || instanceType == null )
        //        return;
        //    string strKey = Convert.ToString(keyValue);
        //    Dictionary<string , InstanceBufferItem> list = this.GetItems(instanceType);
        //    if (list != null)
        //    {
        //        InstanceBufferItem item = list[strKey];
        //        if (item != null)
        //        {
        //            list.Remove( strKey );
        //            _InstanceMap.Remove(strKey);
        //        }
        //    }
        //}

        private struct OuterReferenceItem
        {
            public OuterReferenceItem(object parent, string propertyName)
            {
                this.ParentInstance = parent;
                this.PropertyName = propertyName;
            }
            public object ParentInstance ;
            public string PropertyName ;
            public override string ToString()
            {
                return ParentInstance.ToString() + " " + PropertyName;
            }
        }



        private Dictionary<object, List<OuterReferenceItem>> _OuterReferences 
            = new Dictionary<object, List<OuterReferenceItem>>();

        public void AddOuterReference(object instance, object parentInstance , string propertyName )
        {
            if ( this.IsGlobal == false || instance == null || parentInstance == null)
            {
                return;
            }
            if (_OuterReferences.ContainsKey(instance))
            {
                _OuterReferences[instance].Add(
                    new OuterReferenceItem(parentInstance, propertyName));
            }
            else
            {
                List<OuterReferenceItem> list = new List<OuterReferenceItem>();
                list.Add(new OuterReferenceItem(parentInstance, propertyName));
                _OuterReferences[instance] = list;
            }
        }

        private bool enableRemoveOuterReferenceInfo = true;
        public void RemoveOuterReferenceInfo(object instance, object parentInstance)
        {
            if (instance == null
                || parentInstance == null
                || enableRemoveOuterReferenceInfo == false)
            {
                return;
            }
            if (_OuterReferences.ContainsKey(instance))
            {
                List<OuterReferenceItem> items = _OuterReferences[instance];
                foreach (OuterReferenceItem item in items)
                {
                    if (item.ParentInstance == parentInstance)
                    {
                        items.Remove(item);
                        if (items.Count == 0)
                        {
                            _OuterReferences.Remove(instance);
                        }
                        break;
                    }
                }
            }//if
        }


        private static System.LocalDataStoreSlot flagRunningDeleteOuterReference = System.Threading.Thread.AllocateDataSlot();

        public static bool RunningDeleteOuterReference
        {
            get
            {
                object obj = System.Threading.Thread.GetData(flagRunningDeleteOuterReference);
                if( obj != null )
                    return Convert.ToBoolean( obj );
                else
                    return false ;
            }
            set
            {
                System.Threading.Thread.SetData(flagRunningDeleteOuterReference, value);
            }
        }

        public void DeleteOuterReference( object instance , ORMEngine engine )
        {
            if (instance == null || engine == null )
                return;
            if ( _OuterReferences.ContainsKey( instance ))
            {
                RunningDeleteOuterReference = true;
                List<OuterReferenceItem> items = _OuterReferences[instance];
                foreach (OuterReferenceItem item in items)
                {
                    if (item.PropertyName == null)
                    {
                        if (item.ParentInstance is System.Collections.IList)
                        {
                            IList lst = (IList)item.ParentInstance;
                            int index = lst.IndexOf(instance);
                            if (index >= 0)
                            {
                                lst.RemoveAt(index);
                            }
                        }
                    }
                    else
                    {
                        ORMTypeMappingHelper helper = engine.MappingHelpers[item.ParentInstance.GetType()];
                        if (helper != null)
                        {
                            object obj = helper.GetPropertyValue(item.ParentInstance, item.PropertyName);
                            if (obj == instance)
                            {
                                helper.SetPropertyValue(item.ParentInstance, item.PropertyName, null);
                            }
                        }
                    }
                }//
                _OuterReferences.Remove(instance);
                RunningDeleteOuterReference = false;
            }//if
        }

        public void Clear(Type instanceType)
        {
            if (instanceType != null)
            {
                foreach (Type key in _Buffer.Keys)
                {
                    if (key.Equals(instanceType) || key.IsSubclassOf(instanceType))
                    {
                        Dictionary<string, InstanceBufferItem> items = _Buffer[key];
                        _Buffer.Remove(key);

                        foreach (string strKey in items.Keys )
                        {
                            InstanceBufferItem item = items[strKey];
                            if (_OuterReferences.ContainsKey(item.Instance))
                            {
                                _OuterReferences.Remove(item.Instance);
                            }
                            if (_InstanceMap.ContainsKey(item.Instance))
                            {
                                _InstanceMap.Remove(item.Instance);
                            }
                        }//foreach
                        break;
                    }//if
                }//foreach
            }//if
        }

        public void Clear()
        {
            _InstanceMap.Clear();
            _Buffer.Clear();
            _OuterReferences.Clear();
            GC.Collect();
        }

        public int InstanceCount
        {
            get
            {
                return _InstanceMap.Count;
            }
        }

    }
}//public class ORMInstanceBuffer